catalogue: align published method lists with shipped bundles; backfill index fields - #458
Merged
Merged
Conversation
…l index fields Audited all 29 catalogue apps against three published surfaces (catalogue.json, apps/<id>/metadata.json, the appstore-meta API) and against ground truth: each bundle's signed manifest `exposes` list, cross-checked with the installed app's own `<app>.help` on a clean Linux node. Twelve apps published a method list that did not match the app they ship. Methods - io.pilot.orthogonal: drop orthogonal.check / .transactions / .usage. The v0.1.1 bundle exposes 7 methods and none of these three. orthogonal is metered, so these were exactly the calls an agent would make to check its own spend — the method that does exist is orthogonal.balance. - Document methods the bundles expose but the listing omitted: agentphone.setup, agentphone.status; bowmark.balance; kinetic.balance; rentahuman.balance; ideon-free.generate, ideon-free.poll; and <app>.help for didit, insforge, mysql, postgres, tldr. Summaries are each app's own runtime help text, not new copy. wallet.hookPreSendMessage / .hookPostRecvMessage are deliberately left undocumented — they are daemon lifecycle hooks, not user-callable methods, and no other app documents its hooks. Names, so the same app is called the same thing everywhere - mysql: "Mysql" -> "MySQL" - tldr: "Tldr" -> "tldr" - ideon-free: "Ideon-free" -> "Ideon (Free)", and the index display_name was empty, so `pilotctl appstore catalogue` printed its whole description paragraph in the name column. Index fields - metadata_sha256 for io.pilot.agentphone was stale, not wrong: the pin said d1b5fea7 while both the repo file and raw.githubusercontent served 452d988a. `pilotctl appstore view io.pilot.agentphone` therefore warned and fell back to the teaser. Re-pinned, along with every metadata.json touched here. - categories were empty for agentphone, miren, orthogonal, otto, plainweb and postgres; filled from each listing. - license was empty for orthogonal; set to MIT to match its listing. - bundle_size was 0 for agentphone, ideon-free, miren, otto, plainweb and postgres, and wrong for sixtyfour (4898153 declared, 5058693 served). All set from the bytes actually served at each bundle_url. - changelog head trailed the released version for agentphone (v0.3.0 vs v0.3.1) and orthogonal (v0.1.0 vs v0.1.1). catalogue.json stays "version": 2 — `loadCatalogue` fail-closes on any other value. io.telepat.ideon-free/metadata.json is emitted by Go, whose < escaping a Python round-trip cannot reproduce, so it was edited textually to keep the diff to the intended lines. Verified: all 28 metadata.json parse; every metadata_sha256 matches its file; zero remaining differences between documented methods and shipped `exposes`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… listing Four more index entries had both fields empty on every surface, so the earlier catalogue-vs-listing drift check could not see them — nothing disagreed because nothing was set anywhere. Values come from the `listing` block of submissions/<id>/submission.json in pilot-protocol/app-template, which is what the publish pipeline reads: io.pilot.insforge Apache-2.0 developer-tools, data, storage io.pilot.mysql GPL-2.0 database, data, sql io.pilot.didit Proprietary identity, verification, compliance, security, kyc io.pilot.tldr MIT developer-tools, documentation, cli insforge's submission spells its categories "Developer Tools" / "Data & Storage"; normalised here to the catalogue's existing all-lowercase vocabulary, which already contains developer-tools, data and storage. io.telepat.ideon-free is left as-is: it is third-party, has no submissions/ directory in app-template, and its licence is not ours to assert. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The signature is over the exact bytes of catalogue.json, so the listing and index changes on this branch invalidated it. pilotctl fail-closes on a bad signature — it rejects the whole catalogue rather than one entry — so the detached signature has to travel with the data, the same way #447 did it. Signed with the catalogue key; sign-catalogue refuses any key that does not match the public key embedded in internal/catalogtrust, so acceptance is itself proof the right key was used. Verified: cmd/pilotctl TestLoadCatalogue_VerifiesSignedRepoCatalogue passes again, along with FailsClosedWithoutSignature and FailsOnTamper; and a released v1.13.9 pilotctl loads the re-signed catalogue from disk and lists all 29 apps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
govulncheck resolves the toolchain from go-version-file: go.mod, so the 1.25.12 pin meant CI scanned against a stdlib with five disclosed vulnerabilities and failed every PR opened against the repo today, whether or not it touched Go: GO-2026-6218 net/url GO-2026-6090 crypto/tls GO-2026-6088 encoding/xml GO-2026-5972 encoding/asn1 GO-2026-5026 net/http All five are standard-library only and all five are fixed in go1.25.13, so the patch bump clears the whole set. main has not been scanned since 2026-08-09, before these were published, which is why it still shows green. This is separable from the catalogue work in the rest of the branch and can be cherry-picked out or landed on its own; it is here because the branch cannot go green without it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
✅ Signed — safe to merge
The catalogue is served straight off
mainviaraw.githubusercontent.com, andpilotctlfail-closes on a bad signature: it rejects the entire catalogue, not one entry. So the listing changes here invalidatedcatalogue.json.sigand the branch could not be merged until it was re-signed. That is done.catalogue/catalogue.json.sigis regenerated with the catalogue key.sign-cataloguerefuses any key that does not match the public key embedded ininternal/catalogtrust, so acceptance is itself proof the right key was used.Verified three ways:
cmd/pilotctlTestLoadCatalogue_VerifiesSignedRepoCataloguepasses again, alongsideFailsClosedWithoutSignatureandFailsOnTamper— the tests that were red on this branch.pilotctl(not a build from this branch) loads the re-signed catalogue from disk and lists all 29 apps.catalogue signature: catalogtrust: catalogue signature verification failed, with no apps listed at all.That last point is worth keeping: CI would have caught a merge with a stale signature regardless, because
GoandArchitecture gatesboth run the signature test.One separable commit
go.mod: bump to 1.25.13is unrelated to the catalogue.govulncheckresolves its toolchain fromgo-version-file: go.mod, so the1.25.12pin meant CI scanned against a stdlib carrying five disclosed vulnerabilities (GO-2026-6218,-6090,-6088,-5972,-5026) and failed every PR opened against the repo today, Go-touching or not. All five are stdlib-only and all five are fixed ingo1.25.13.mainstill shows green only because it has not been scanned since 2026-08-09, before these were published. Happy to split it out if you would rather land it on its own.Appendix: what the red CI looked like before signing
TestLoadCatalogue_VerifiesSignedRepoCataloguefails withThat single failure is what reds Go (ubuntu), Go (macos) and Architecture gates — the last runs
go test ./pkg/... ./cmd/... ./internal/..., which includes the same test. This is the repo refusing to accept a catalogue whose signature does not match, which is exactly the protection this PR depends on. It cannot be merged wrong.The precedent is #447, the last catalogue change: it committed
catalogue/catalogue.json.sigalongsidecatalogue.jsonand CI passed. Same is needed here — re-sign on this branch and the three checks go green.govulncheckis unrelated to this change. It reports 5 Go standard library vulnerabilities (GO-2026-5026 and others, fixed in go1.25.13) reached throughnet/http. This PR touches no Go code; the check passed on #457 only because that run predates the disclosure, andmain's own nightly has been failing for several nights. It needs a Go toolchain bump, separately from this PR.What this fixes
I audited all 29 catalogue apps across three published surfaces —
catalogue.json,apps/<id>/metadata.json, and the appstore-meta API — against ground truth: each bundle's signed manifestexposeslist, cross-checked with the installed app's own<app>.helpon a clean Linux node.Twelve apps published a method list that did not match the app they ship.
Methods
io.pilot.orthogonal— removed three methods that do not exist. The v0.1.1 bundle exposes 7 methods;orthogonal.check,orthogonal.transactionsandorthogonal.usageare not among them.orthogonalis metered, so these were precisely the calls an agent would make to check its own spend. The method that does exist isorthogonal.balance— which was itself undocumented, and is now listed.agentphone.setup,agentphone.status;bowmark.balance;kinetic.balance;rentahuman.balance;ideon-free.generate,ideon-free.poll; and<app>.helpfordidit,insforge,mysql,postgres,tldr.Summaries are each app's own runtime help text, not new marketing copy.
wallet.hookPreSendMessage/.hookPostRecvMessage. They are daemon lifecycle hooks rather than user-callable methods, and no other app documents its hooks. (Webhook methods, which are user-facing, are already documented everywhere they exist.)The
balancecases matter on their own: three spend-bearing apps shipped a way to check remaining budget that an agent could not discover from any published surface.Names — so the same app is called the same thing everywhere
io.pilot.mysqlMysqlMySQLio.pilot.tldrTldrtldrio.telepat.ideon-freeIdeon-free(index: empty)Ideon (Free)The API and website already said
MySQL/tldr/Ideon (Free); the catalogue and metadata disagreed. Because ideon-free's indexdisplay_namewas empty,pilotctl appstore catalogueprinted its entire description paragraph in the column where every other app shows a short name.Index fields
metadata_sha256forio.pilot.agentphonewas stale, not wrong. The pin saidd1b5fea7…while both the repo file and raw.githubusercontent serve452d988a…. Someone edited the listing without re-pinning, sopilotctl appstore view io.pilot.agentphoneprinted:metadata.jsontouched here.categoriesempty → filled from each listing:agentphone,miren,orthogonal,otto,plainweb,postgres.licenseempty fororthogonal→MIT, matching its listing.bundle_sizewas0foragentphone,ideon-free,miren,otto,plainweb,postgres, and wrong forsixtyfour(declared 4,898,153 B, serves 5,058,693 B). All set from the bytes actually served at eachbundle_url. Bundle checksums were all correct — only the size field was stale.agentphone(v0.3.0 vs shipped v0.3.1) andorthogonal(v0.1.0 vs v0.1.1).Not in this PR
Four apps cannot run on Linux at all —
cosift,walletandideon-freehave no per-platformbundlesmap, sopilotctlfalls back to their singlebundle_url, which is a darwin build that installs cleanly and then never spawns;slipstreamhas abundlesmap but nolinux/arm64entry, so it correctly hard-errors. Fixing those needs Linux bundles built and published first, so they are deliberately left out rather than half-fixed here. Worth noting the mechanism is sound and already used by 25 of 29 apps — these four are legacy entries that never got one.Verification
metadata.jsonparsemetadata_sha256matches its file on diskexposes(was 12)catalogue.jsonstays"version": 2—loadCataloguefail-closes on anything elseio.telepat.ideon-free/metadata.jsonis emitted by Go (whose<→<escaping a Python round-trip cannot reproduce) so it was edited textually to keep the diff to the intended lines🤖 Generated with Claude Code